/**
 * @file app.c
 * @author zhaitao (zhaitao.as@outlook.com)
 * @brief 
 * @version 0.1
 * @date 2021-04-30
 * 
 * @copyright zhaitao.as@outlook.com (c) 2021
 * 
 */
#include "app.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Private define ------------------------------------------------------------*/

#define DEFAULT_VOLTAGE 1000
#define DEFAULT_CURRENT 50
#define DEFAULT_FREQ 1000
#define DEFAULT_PWM 50
#define DEFAULT_VGAIN 20
#define DEFAULT_IGAIN 5

#define MAX_VOLTAGE 30
#define MAX_CURRENT 1
#define MAX_FREQ 500000
#define MAX_PWM 100

#define RX_LEN 8

/* Const variables -----------------------------------------------------------*/

const char MPD_MSG_HELP[] = "\
>\tUsage: \r\n\
\t\tJust Enter: print state\r\n\
\t\tV21000\t(V+mV for Voltage)\r\n\
\t\tI500\t(I+mA for Current)\r\n\
\t\tON/OFF\t(Switch Output)\r\n";

const char PROMPT_SIGN[] = "$ ";
const char RESPONSE_SIGN[] = "> ";

struct MPD_Response_Typedef {
	int id;
	char *msg;
};

#define MPD_ERRID_OK 0
#define MPD_ERRID_INVALID_COMMAND 1
#define MPD_ERRID_SET_HIGH_VOLTAGE 2
#define MPD_ERRID_SET_HIGH_CURRENT 3
#define MPD_ERRID_SET_HIGH_PWMFREQ 4
#define MPD_ERRID_SET_HIGH_PWMDUTY 5

char MPD_MSG_OK[] = "OK";
char MPD_MSG_INVALID_COMMAND[] = "Invalid Command, H/h/? for Help";
char MPD_MSG_SET_HIGH_VOLTAGE[] = "Voltage Cannot set to over 30V";
char MPD_MSG_SET_HIGH_CURRENT[] = "Current Cannot set to over 0.5A";
char MPD_MSG_SET_HIGH_PWMFREQ[] = "PWM Cannot set to over 100KHz";
char MPD_MSG_SET_HIGH_PWMDUTY[] = "PWM Cannot set to over 100%%";

uint8_t UartIRQFlag;
uint8_t uartRxBuffer[256];

struct MPD_Response_Typedef MPD_Response_List[] = {
    {MPD_ERRID_OK, MPD_MSG_OK},
    {MPD_ERRID_INVALID_COMMAND, MPD_MSG_INVALID_COMMAND},
    {MPD_ERRID_SET_HIGH_VOLTAGE, MPD_MSG_SET_HIGH_VOLTAGE},
    {MPD_ERRID_SET_HIGH_CURRENT, MPD_MSG_SET_HIGH_CURRENT},
    {MPD_ERRID_SET_HIGH_PWMFREQ, MPD_MSG_SET_HIGH_PWMFREQ},
    {MPD_ERRID_SET_HIGH_PWMDUTY, MPD_MSG_SET_HIGH_PWMDUTY},
};

struct MPD_Command_Typedef {
	char *cn; /* command name*/
};

/* Extern variables ----------------------------------------------------------*/

extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;

/* Private variables ---------------------------------------------------------*/
uint32_t app_voltage_setting = 0;
uint32_t app_current_setting = 0;
uint32_t app_freq_setting = 0;
uint32_t app_pwm_setting = 0;

uint32_t app_voltage_sense = 0;
uint32_t app_current_sense = 0;

uint8_t app_pwm_polarity = 0;
uint32_t app_pwm_freq = 0;
uint32_t app_pwm_duty = 0;

uint8_t rxbuffer[RX_LEN] = {0};
uint8_t rxFlag = 0;
int8_t rxCnt = -1;

uint8_t app_debug = 0;
#define BUFFER_SIZE (255)

/* Private functions declear -------------------------------------------------*/
uint8_t app_Initial(void);
int app_doString(char *str);
void app_uartResponse(uint8_t id);
void app_SendMessage(char *str, uint32_t len);
uint8_t app_setVoltage(float v);
uint8_t app_setCurrent(float i);
double app_getVoltage(void);
double app_getCurrent(void);
/* Exported functions ---------------------------------------------------------*/

/**
 * @brief start main loop
 * 
 * @return uint8_t initial failed state
 */
uint8_t app_Start(void)
{
	// turn off output

	// read eeprom data

	// if there is no data in eeprom, get default data, set eeprom, set global

	// set dac
	bsp_dacInitial();
	HAL_Delay(100);

	app_setVoltage(0.1);
	HAL_Delay(100);

	app_setCurrent(0.01);
	HAL_Delay(100);

	bsp_adcInitial();

	/* Initial Shell */
	do {
		ntshell_init(
		    &ntshell,
		    func_read,
		    func_write,
		    func_callback,
		    (void *)&ntshell);
		ntshell_set_prompt(&ntshell, "#");
	} while (0);

	__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart2, (uint8_t *)uartRxBuffer, 255);

	while (1) {
		/* Detect Uart Interrupt, then excute shell */
		if (UartIRQFlag) {

			app_dprintf("get irq\r\n");

			uint8_t data_length = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); //计算接收到的数据长度

			app_dprintf("Receive Data(length = %d): ", data_length);
			//HAL_UART_Transmit(&huart2, uartRxBuffer, data_length, 0x200); //测试函数：将接收到的数据打印出去
			app_dprintf((const char *)uartRxBuffer);
			app_dprintf("\r\n");

			// excute shell
			vtrecv_execute(&(ntshell.vtrecv), uartRxBuffer, data_length);

			//清零接收缓冲区
			memset(uartRxBuffer, 0, data_length);
			data_length = 0;
			//重启开始DMA传输 每次255字节数据
			HAL_UART_Receive_DMA(&huart2, (uint8_t *)uartRxBuffer, 255);
			UartIRQFlag = 0;
		}

		HAL_Delay(1);
	}
}

/**
 * @brief stop main loop
 * 
 * @return uint8_t 
 */
uint8_t app_Stop(void)
{
	return 0;
}

/**
 * @brief set output voltage
 * 
 * @param v 
 * @return uint8_t 
 */
uint8_t app_setVoltage(float v)
{
	if (v > MAX_VOLTAGE) {
		return -1;
	}

	double vdac = v / DEFAULT_VGAIN;
	dprintf("dacV output %f \r\n", vdac);

	bsp_setDacCV(vdac);

	app_voltage_setting = v * 1000;

	return 0;
}

/**
 * @brief set output current
 * 
 * @param i 
 * @return uint8_t 
 */
uint8_t app_setCurrent(float i)
{
	if (i > MAX_CURRENT) {
		return -1;
	}

	double vdac = i * DEFAULT_IGAIN;
	dprintf("dacC output %f \r\n", vdac);

	bsp_setDacCC(vdac);

	app_current_setting = i * 1000;

	return 0;
}

/**
 * @brief get real output voltage
 * 
 * @return double 
 */
double app_getVoltage(void)
{
	double adc = bsp_getAdcCV();

	dprintf("get voltage adc %f\r\n", adc);

	return adc * 20.0;
}

/**
 * @brief get real output current from adc
 * 
 * @return double 
 */
double app_getCurrent(void)
{
	double adc = bsp_getAdcCC();

	dprintf("get voltage adc %f\r\n", adc);

	return adc / 5.0;
}

uint8_t app_setFreq(uint32_t);
uint32_t app_getFreq(uint32_t);
uint8_t app_setPwm(uint32_t);
uint32_t app_getPwm(uint32_t);

/**********************************
 * OLED Display
**********************************/

/**
 * @brief Show Start Up Progress Bar
 * 
 * @return uint8_t 
 */
uint8_t app_showStartUpProgress(void);
uint8_t app_drawUI(void);
uint8_t app_drawVoltageSetting(void);
uint8_t app_drawVoltageReal(void);
uint8_t app_drawCurrentSetting(void);
uint8_t app_drawCurrentReal(void);
uint8_t app_drawFreq(void);
uint8_t app_drawPwm(void);

/**********************************
 * Data Storage
**********************************/
/**
 * @brief check EEPROM 0x08080000 == 0x32107654, 0xba98fedc
 * 
 * @return uint8_t 
 */
uint8_t app_checkEepromFlag(void);
uint8_t app_setEepromFlag(void);
uint8_t app_loadDefaultSetting(void);
uint8_t app_loadLastSetting(void);
uint8_t app_saveSetting(void);

/**********************************
 * Message Process
**********************************/

/**
 * @brief Message Callback
 * 
 */
void app_MsgCB(void)
{
	//rxFlag = 1;
	//HAL_UART_Transmit(&huart1, rxbuffer, RX_LEN, 100);
	HAL_UART_Receive_DMA(&huart2, rxbuffer, RX_LEN);
}

/**
 * @brief send message to host
 * 
 * @param str message
 * @param len length
 */
void app_SendMessage(char *str, uint32_t len)
{
	HAL_UART_Transmit(&huart2, (uint8_t *)str, len, 0xfff);
}

/**
 * @brief Message Process
 * 
 * @param str message to be process
 * @return uint8_t 0:OK, other:something wrong
 */
int app_doString(char *str)
{
	int result = 1;

	for (uint8_t i = 0; i < strlen(str); i++) {
		dprintf("%02x ", str[i]);
	}
	dprintf("\r\n");

	/* \r or \n will print current state */
	if (rxbuffer[0] == '\r' || rxbuffer[0] == '\n') {
		char str[32] = {0};

		/* Print Setting Values */
		sprintf(str, ">\tVset=%05.2f, Iset=%05.3f\r\n", app_voltage_setting / 1000.0, app_current_setting / 1000.0);
		app_SendMessage(str, 32);

		/* Print Measured values */
		sprintf(str, ">\tVsns=%05.2f, Isns=%05.3f\r\n", app_getVoltage(), app_getCurrent());
		app_SendMessage(str, 32);

		result = 0;
		goto DOSTRING_END;
	}

	/* Help */
	if (rxbuffer[0] == 'H' || rxbuffer[0] == 'h' || rxbuffer[0] == '?') {
		app_SendMessage((char *)MPD_MSG_HELP, strlen(MPD_MSG_HELP));
		result = 0;
		goto DOSTRING_END;
	}

	/* Power */
	if (rxbuffer[0] == 'O') {
		/* Power ON */
		if (rxbuffer[1] == 'N') {
			dprintf("turn on output\r\n");
			result = 0;
			goto DOSTRING_END;
		}

		/* Power Off */
		if (rxbuffer[1] == 'F') {
			if (rxbuffer[2] == 'F') {
				dprintf("turn off output\r\n");

				result = 0;
				goto DOSTRING_END;
			}
		}
	}

	/* Set Voltage */
	if (rxbuffer[0] == 'V') {

		int len = strlen((char *)rxbuffer);
		char *vstr = malloc(len);

		strcpySub(vstr, (char *)rxbuffer, 1, strlen((char *)rxbuffer));

		float f = strtod(vstr, NULL);

		dprintf("set voltage %f \r\n", f);

		if (f > MAX_VOLTAGE) {
			char str[48];
			sprintf(str, ">\tVoltage Cannot set to over %dV\r\n", MAX_VOLTAGE);
			app_SendMessage(MPD_MSG_SET_HIGH_VOLTAGE, strlen(MPD_MSG_SET_HIGH_VOLTAGE));
			return MPD_ERRID_SET_HIGH_VOLTAGE;
		}

		app_setVoltage(f);

		free(vstr);

		result = 0;
		goto DOSTRING_END;
	}

	/* Set Current */
	if (rxbuffer[0] == 'I') {

		int len = strlen((char *)rxbuffer);
		char *vstr = malloc(len);

		strcpySub(vstr, (char *)rxbuffer, 1, strlen((char *)rxbuffer));

		float f = strtod(vstr, NULL);

		dprintf("set current %f \r\n", f);

		if (f > MAX_CURRENT) {
			char str[48];
			sprintf(str, ">\tCurrent Cannot set to over %dA\r\n", MAX_CURRENT);
			app_SendMessage(MPD_MSG_SET_HIGH_CURRENT, strlen(MPD_MSG_SET_HIGH_CURRENT));
			return MPD_ERRID_SET_HIGH_CURRENT;
		}

		app_setCurrent(f);

		free(vstr);

		result = 0;
		goto DOSTRING_END;
	}

	/* Get state */
	if (rxbuffer[0] == 'G') {

		/* Get Voltage */
		if (rxbuffer[1] == 'V') {
			double adc = app_getVoltage();

			dprintf("get output voltage %f\r\n", adc);

			char str[32];
			sprintf(str, "%05.2fV\r\n", adc);
			app_SendMessage(str, strlen(str));

			result = 0;
			goto DOSTRING_END;
		}

		/* Get Current */
		if (rxbuffer[1] == 'I') {
			double adc = app_getCurrent();
			dprintf("get output current %f\r\n", adc);
			char str[32];
			sprintf(str, "%05.3fA\r\n", adc);
			app_SendMessage(str, strlen(str));

			result = 0;
			goto DOSTRING_END;
		}
	}

	if (rxbuffer[0] == 'P') {

		/* Set PWM Frequence */
		if (rxbuffer[1] == 'F') {
			dprintf("Set PWM Freq\r\n");

			result = 0;
			goto DOSTRING_END;
		}

		/* Set PWM Duty */
		if (rxbuffer[1] == 'D') {
			dprintf("Set PWM Duty\r\n");

			result = 0;
			goto DOSTRING_END;
		}

		/* Set PWM Polarity */
		if (rxbuffer[1] == 'P') {
			dprintf("Set PWM Polarity\r\n");

			if (rxbuffer[2] != 0) {
				app_pwm_polarity = 1;
			} else {
				app_pwm_polarity = 0;
			}

			result = 0;
			goto DOSTRING_END;
		}

		/* Set PWM Enable */
		if (rxbuffer[1] == 'E') {
			dprintf("Set PWM Enable\r\n");

			if (rxbuffer[2] == '0') {
				dprintf("pwm disable\r\n");
				bsp_PWMEnable(0);
			} else {
				dprintf("pwm enable\r\n");
				bsp_PWMEnable(1);
			}

			result = 0;
			goto DOSTRING_END;
		}

		char msg[64] = ">\tPF-freq, PD-duty, PP-polariy, PE-enable\r\n";
		app_SendMessage(msg, strlen(msg));
		result = 1;
		goto DOSTRING_END;
	}

DOSTRING_END:
	app_uartResponse(result);
	return result;
}

/**
 * @brief General response
 * 
 * @param state 0:OK, !0:Something wrong
 */
void app_uartResponse(uint8_t id)
{
	char sign[64];

	sprintf(sign, "%s\t%s\r\n%s ", RESPONSE_SIGN, MPD_Response_List[id].msg, PROMPT_SIGN);
	app_SendMessage(sign, strlen(sign));
}

void app_dprintf(const char *format, ...)
{
	if (app_debug != 0) {
		char pszBuff[256];
		char dbgfmt[64];

		va_list args;
		va_start(args, format);

		strcpy(dbgfmt, "[DEBUG]\t");
		strcat(dbgfmt, format);

		vsprintf(pszBuff, dbgfmt, args);
		printf(pszBuff);
		va_end(args);
	}
}